Powershell scripts/Read Azure Storage Transaction Metrics/Read-AzStorageTransactions.ps1 (258 lines of code) (raw):
<#PSScriptInfo
.VERSION 1.2
.GUID 8acc693a-076b-4e28-8e9d-288f27ebbaeb
.AUTHOR ndicola@microsoft.com
.COMPANYNAME Microsoft
.COPYRIGHT Microsoft
.TAGS
.LICENSEURI
.PROJECTURI
https://github.com/JimGBritt/Azure-Security-Center
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
March 15, 2020 1.1
- Updated to use Get-AzAccessToken
#>
<#
.SYNOPSIS
This script will enumerate all storage accounts and get the metrics for the last week (aggregated by day)
so that you can calculate ASC Storage ATP costs.
.DESCRIPTION
This script takes a SubscriptionID, ResourceGroup as parameters, analyzes the subscription or
specific ResourceGroup defined for the resources specified in $Resources, and gets all storage accounts,
then enumerates the transactions metrics for each.
.PARAMETER SubscriptionId
The subscriptionID of the Azure Subscription that contains the resources you want to analyze
.PARAMETER ExportFile
PATH\File name to export the output to. If not used, the working directory of the script will be leveraged as the export directory base folder
.PARAMETER Tenant
Use the -Tenant parameter to bypass the subscriptionID requirement
Note: Cannot use in conjunction with -SubscriptionID
.EXAMPLE
.\Read-AzStorageTransactions.ps1 -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512" -ResourceGroup "RGName"
Enumerate metrics for all storage accounts in the Resource group in the specified Subscription.
.EXAMPLE
.\Read-AzStorageTransactions.ps1 -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512"
Enumerate metrics for all storage accounts in the specified Subscription.
.EXAMPLE
.\Read-AzStorageTransactions.ps1 -Tenant
Enumerate metrics for all storage accounts in all subscriptions.
.NOTES
AUTHOR: Nicholas DiCola Principal Group PM Manager - Security CxE
LASTEDIT: March 15, 2021 1.2
- Initial Release
- Special tanks to Jim Britt (Microsoft) https://twitter.com/JimBrittPhotos for the policy script which has many of the discovery functions!
- Added support for Blob and File transactions only
- Added support for Get-AzAccessToken
.LINK
This script posted to and discussed at the following locations:
https://github.com/Azure/Azure-Security-Center
#>
[cmdletbinding(
DefaultParameterSetName='Default'
)]
param
(
# Export Directory Path for output - if not set - will default to script directory
[Parameter(ParameterSetName='Default',Mandatory = $False)]
[Parameter(ParameterSetName='Subscription')]
[Parameter(ParameterSetName='Tenant')]
[string]$ExportFile,
# Provide SubscriptionID to bypass subscription listing
[Parameter(ParameterSetName='Subscription')]
[guid]$SubscriptionId,
# Tenant switch to bypass subscriptionId requirement
[Parameter(ParameterSetName='Tenant')]
[switch]$Tenant=$False
)
# FUNCTIONS
# Build out the body for the GET / PUT request via REST API
function BuildBody
(
[parameter(mandatory=$True)]
[string]$method
)
{
$BuildBody = @{
Headers = @{
Authorization = "Bearer $($token.token)"
'Content-Type' = 'application/json'
}
Method = $Method
UseBasicParsing = $true
}
$BuildBody
}
# Function used to build numbers in selection tables for menus
function Add-IndexNumberToArray (
[Parameter(Mandatory=$True)]
[array]$array
)
{
for($i=0; $i -lt $array.Count; $i++)
{
Add-Member -InputObject $array[$i] -Name "#" -Value ($i+1) -MemberType NoteProperty
}
$array
}
# MAIN SCRIPT
if ($MyInvocation.MyCommand.Path -ne $null)
{
$CurrentDir = Split-Path $MyInvocation.MyCommand.Path
}
else
{
# Sometimes $myinvocation is null, it depends on the PS console host
$CurrentDir = "."
}
Set-Location $CurrentDir
If(!($ExportFile))
{
$ExportFile = "$CurrentDir\azstoragemetrics.csv"
}
#Variable Definitions
$SubScriptionsToProcess = $null
# Login to Azure - if already logged in, use existing credentials.
Write-Host "Authenticating to Azure..." -ForegroundColor Cyan
try
{
$AzureLogin = Get-AzSubscription
$currentContext = Get-AzContext
$token = Get-AzAccessToken
if($Token.ExpiresOn -lt $(get-date))
{
"Logging you out due to cached token is expired for REST AUTH. Re-run script"
$null = Disconnect-AzAccount
}
}
catch
{
$null = Login-AzAccount
$AzureLogin = Get-AzSubscription
$currentContext = Get-AzContext
$token = Get-AzAccessToken
}
# Authenticate to Azure if not already authenticated
If($AzureLogin -and !($SubscriptionID) -and !($Tenant))
{
[array]$SubscriptionArray = Add-IndexNumberToArray (Get-AzSubscription)
[int]$SelectedSub = 0
# use the current subscription if there is only one subscription available
if ($SubscriptionArray.Count -eq 1)
{
$SelectedSub = 1
}
# Get SubscriptionID if one isn't provided
while($SelectedSub -gt $SubscriptionArray.Count -or $SelectedSub -lt 1)
{
Write-host "Please select a subscription from the list below"
$SubscriptionArray | Select-Object "#", Id, Name | Format-Table
try
{
$SelectedSub = Read-Host "Please enter a selection from 1 to $($SubscriptionArray.count)"
}
catch
{
Write-Warning -Message 'Invalid option, please try again.'
}
}
if($($SubscriptionArray[$SelectedSub - 1].Name))
{
$SubscriptionName = $($SubscriptionArray[$SelectedSub - 1].Name)
}
elseif($($SubscriptionArray[$SelectedSub - 1].SubscriptionName))
{
$SubscriptionName = $($SubscriptionArray[$SelectedSub - 1].SubscriptionName)
}
write-verbose "You Selected Azure Subscription: $SubscriptionName"
if($($SubscriptionArray[$SelectedSub - 1].SubscriptionID))
{
[guid]$SubscriptionID = $($SubscriptionArray[$SelectedSub - 1].SubscriptionID)
}
if($($SubscriptionArray[$SelectedSub - 1].ID))
{
[guid]$SubscriptionID = $($SubscriptionArray[$SelectedSub - 1].ID)
}
}
if($SubscriptionId -and !($Tenant))
{
Write-Host "Selecting Azure Subscription: $($SubscriptionID.Guid) ..." -ForegroundColor Cyan
$Null = Select-AzSubscription -SubscriptionId $SubscriptionID.Guid
}
if($Tenant)
{
$SubScriptionsToProcess = Get-AzSubscription -TenantId $($token).TenantId
}
# Gather a list of resources supporting Azure Diagnostic logs and metrics and display a table
try
{
if($SubscriptionId -and !($Tenant))
{
Write-Host "Gathering a list of Storage Accounts from Azure Subscription ID " -NoNewline -ForegroundColor Cyan
Write-Host "$SubscriptionId..." -ForegroundColor Yellow
$ResourcesToCheck = Get-AzStorageAccount
}
elseif($Tenant -and !($SubscriptionId))
{
if($Tenant){Write-Host "Gathering a list of Storage Accounts from Azure AD Tenant " -ForegroundColor Cyan}
Write-Host "A total of $($SubScriptionsToProcess.count) subscriptions to process..."
foreach($Sub in $SubScriptionsToProcess)
{
$SubIDToProcess = $($Sub.SubscriptionId)
$SubName = $($Sub.Name)
$SelectedSub = Select-AzSubscription -SubscriptionID $SubIDToProcess
Write-Host "Getting Storage Accounts from Subscription: $SubName"
$ResourcesToCheck += Get-AzStorageAccount
}
}
#Loop through all storage accounts
# GET https://management.azure.com/subscriptions/44e4eff8-1fcb-4a22-a7d6-992ac7286382/resourceGroups/CxE-Nicholas/providers/Microsoft.Storage/storageAccounts/cxenicholas/providers/microsoft.insights/metrics?api-version=2018-01-01&metricnames=transactions×pan=2020-01-05T00:00:00Z/2020-01-05T23:59:59Z&interval=P1D
$Start = (Get-Date).AddDays(-7) | Get-Date -Hour 0 -Minute 0 -Second 0 | Get-Date -Format "yyyy-MM-ddThh:mm:ssZ"
$End = (Get-Date).AddDays(-1) | Get-Date -Hour 23 -Minute 59 -Second 59 | Get-Date -Format "yyyy-MM-ddThh:mm:ssZ"
$body = BuildBody GET
$records = @()
$total = 0
Write-Host "Getting metrics for $($ResourcesToCheck.Count) storage accounts"
foreach($storageAccount in $ResourcesToCheck){
$Bloburi = "https://management.azure.com$($storageAccount.Id)/blobServices/default/providers/microsoft.insights/metrics?api-version=2018-01-01&metricnames=transactions×pan=$start/$end&interval=P1D"
$Blobtransactions = Invoke-RestMethod -Method GET -Uri $Bloburi -Headers ($body.Headers)
$Fileuri = "https://management.azure.com$($storageAccount.Id)/fileServices/default/providers/microsoft.insights/metrics?api-version=2018-01-01&metricnames=transactions×pan=$start/$end&interval=P1D"
$FileTransactions = Invoke-RestMethod -Method GET -Uri $Fileuri -Headers ($body.Headers)
foreach($result in $Blobtransactions.value.timeSeries.data){
$result | Add-Member -MemberType NoteProperty -Name "MetricType" -Value "Blob"
$result | Add-Member -MemberType NoteProperty -Name "StorageAccount" -Value $storageAccount.StorageAccountName
$result | Add-Member -MemberType NoteProperty -Name "ResourceGroup" -Value $storageAccount.ResourceGroupName
$result | Add-Member -MemberType NoteProperty -Name "Location" -Value $storageAccount.Location
$result | Add-Member -MemberType NoteProperty -Name "Subscription" -Value ($storageAccount.id).split("/")[2]
$result | Add-Member -MemberType NoteProperty -Name "Id" -Value $storageAccount.id
$records += $result
$total += $result.total
}
foreach($result in $Filetransactions.value.timeSeries.data){
$result | Add-Member -MemberType NoteProperty -Name "MetricType" -Value "File"
$result | Add-Member -MemberType NoteProperty -Name "StorageAccount" -Value $storageAccount.StorageAccountName
$result | Add-Member -MemberType NoteProperty -Name "ResourceGroup" -Value $storageAccount.ResourceGroupName
$result | Add-Member -MemberType NoteProperty -Name "Location" -Value $storageAccount.Location
$result | Add-Member -MemberType NoteProperty -Name "Subscription" -Value ($storageAccount.id).split("/")[2]
$result | Add-Member -MemberType NoteProperty -Name "Id" -Value $storageAccount.id
$records += $result
$total += $result.total
}
}
Write-Host "Exporting $($records.Count) records to $($ExportFile)"
$records | Export-csv $ExportFile
Write-Host "Total Blob and File Transactions is: $($Total)"
Write-Host "Total Transactions / 10k is: $($Total/10000)"
Write-Host "Estimate Cost is (`$0.02/10k transactions): `$$($Total/10000*0.02) for the last seven days"
}
catch
{
if($SubscriptionId)
{
Throw write-host "No Storage Accounts available in selected subscription $SubscriptionID" -ForegroundColor Red
}
}
Write-Host "Complete" -ForegroundColor Green